﻿using Biotech.Log;
using System.Diagnostics;
using System.Reflection;
using Biotech.Common.Driver;

namespace project.B12.Model.Error
{
    /// <summary>
    /// 上位机异常
    /// </summary>
    public sealed class SoftwareException : Exception
    {
        public ErrCodeModel FaultCode { get; set; } = new ErrCodeModel();

        public string? MemberName { get; set; }

        public SoftwareException(object code, params object?[] args)
        {
            GetStack();
            FaultCode.ErrCode = code;
            FaultCode.ArgsString = string.Join(",", args);
            GetErrorCodeAttribute(code, args); // TODO 不需要，只需要记录code和args
            HandlingException();
        }

        public SoftwareException(object code, Exception innerException, params object?[] args) : base(null, innerException)
        {
            GetStack();
            FaultCode.ErrCode = code;
            FaultCode.ArgsString = string.Join(",", args);
            GetErrorCodeAttribute(code, args);
            HandlingException();
        }

        public SoftwareException(string message)
        {
            GetStack();
            FaultCode.Message = message;
            HandlingException();
        }

        public SoftwareException(object code, string message, Exception innerException) : base(message, innerException)
        {
            GetStack();
            FaultCode.ErrCode = code;
            FaultCode.Message = message;
            HandlingException();
        }

        public SoftwareException(string message, Exception innerException) : base(message, innerException)
        {
            GetStack();
            FaultCode.Message = message;
            HandlingException();
        }

        public SoftwareException(Exception e) : base(e.Message, e)
        {
            GetStack();
            FaultCode.Message = e.Message;
            HandlingException();
        }

        // TODO 好像没用
        /// <summary>
        /// 获取异常的方法名、路径
        /// </summary>
        private void GetStack()
        {
            var stackTrace = new StackTrace();
            MemberName = stackTrace.GetFrame(1)?.GetMethod()?.Name;
            var sourceFilePath = stackTrace.GetFrame(1)?.GetFileName();
            var sourceLineNumber = stackTrace.GetFrame(1)?.GetFileColumnNumber();
            Source = System.IO.Path.GetFileName(sourceFilePath) + ": " + sourceLineNumber;
        }

        /// <summary>
        /// 处理异常
        /// </summary>
        private void HandlingException(Exception? exception = null)
        {
            switch (exception ?? InnerException)
            {
                case SoftwareException _:
                    FaultCode.ErrCode ??= B12ErrCode.ExcuteException;
                    Log();
                    break;

                //case ModuleException ex:
                //    FaultCode.Message =
                //        $"ErrCode:{ex.err},Command:{ex.Command},ModuleName:{ex.ModuleName},Message:{ex.Message}";
                //    FaultCode.ErrCode ??= B12ErrCode.ExcuteException;
                //    Log();
                //    break;

                case ExcuteException ex:
                    FaultCode.Message = $"Node:{ex.NodeId},Command:{ex.Command},ErrCode:{ex.ErrCode},Tips:{ex.Tips},Message:{ex.Message}";
                    FaultCode.ErrCode ??= B12ErrCode.ExcuteException;
                    Log();
                    break;

                case AggregateException ex:
                    {
                        foreach (var item in ex.InnerExceptions)
                        {
                            FaultCode.Message = $"{item.Message}";
                            HandlingException(item);
                            //Log(LogType.Fatal);
                        }

                        break;
                    }
                default:
                    Log(LogType.Fatal);
                    break;
            }
        }

        private void Log(LogType logType = LogType.Error)
        {
            if (FaultCode.ErrCode == null)
            {
                FaultCode.ErrCode = B12ErrCode.Error;
                //GetErrorCodeAttribute(FaultCode.ErrCode);
            }

            AddLogger(logType);
        }

        public delegate void LogEventHandler();

        public static event LogEventHandler? LogEvent;

        private void AddLogger(LogType logType)
        {
            var info = string.IsNullOrEmpty(FaultCode.ArgsString) ? FaultCode.Message : FaultCode.ArgsString;
            var str =
                $"{FaultCode.ErrCode},{info}\r\n" +
                $"Source:{Source},MethodName:{MemberName}\r\n" +
                $"Exception:{this}\r\n";
            if (logType == LogType.Error)
            {
                LoggerFactory.Instance.CurrentLogger.LogError(str);
            }
            else
            {
                LoggerFactory.Instance.CurrentLogger.LogFatal(str);
            }
            // TODO 简略日志

            // TODO 报警信息增加
            LogEvent?.Invoke();
        }

        /// <summary>
        /// 获取错误代码属性
        /// </summary>
        /// <param name="errorCode"></param>
        /// <param name="args"></param>
        private void GetErrorCodeAttribute(object errorCode, params object?[]? args)
        {
            var errorCodeItem = GetErrorCodeItemAttribute(errorCode);
            if (errorCodeItem == null)
            {
                return;
            }
            var errorCodeMessage = errorCodeItem.Message;
            FaultCode.Message = (args == null || args.Length == 0 ? errorCodeMessage : string.Format(errorCodeMessage, args));
            FaultCode.Solution = errorCodeItem.Solution;
            FaultCode.Module = errorCodeItem.Module;
            FaultCode.Name = errorCodeItem.Name;
            FaultCode.Reason = errorCodeItem.Reason;
            FaultCode.Type = errorCodeItem.Type;
        }

        private static ErrorCodeItemAttribute? GetErrorCodeItemAttribute(object errorCode)
        {
            var errorType = errorCode.GetType();
            var fieldInfo = errorType.GetField(Enum.GetName(errorType, errorCode)!);
            var errorCodeItem = fieldInfo?.GetCustomAttribute<ErrorCodeItemAttribute>();
            return errorCodeItem;
        }

        //public static ErrorCodeItemAttribute GetString(string str, ErrorCodeItemAttribute? errorCodeItem)
        public static ErrorCodeItemAttribute? GetString(string str)
        {
            str = str.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)[0];
            var strArr = str.Split(',');
            if (strArr.Length == 0)
                return null;
            if (!Enum.TryParse<B12ErrCode>(strArr[0], true, out var flag))
            {
                return null;
            }
            var errorCodeItem = GetErrorCodeItemAttribute(flag);
            if (errorCodeItem != null)
            {
                errorCodeItem.Message = strArr.Length == 0
                    ? errorCodeItem.Message
                    : string.Format(errorCodeItem.Message, strArr.Skip(1).ToArray());
                errorCodeItem.ErrCode = flag;
            }
            return errorCodeItem;
        }

        //public static (string code, string message) GetString(string str)
        //{
        //    str = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)[0];
        //    var strArr = str.Split(',');
        //    if (strArr.Length == 0)
        //        return (string.Empty, string.Empty);
        //    var errorCodeItem = GetErrorCodeItemAttribute(strArr[0]);
        //    return (strArr[0], strArr.Length == 0 ? errorCodeItem.Message : string.Format(errorCodeItem.Message, strArr.Skip(1).ToArray()));
        //}
    }
}
